using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Microsoft.SharedSource.SqlServer.MDDE.Middleware;
using System.Data.SqlClient;
using System.Threading;

namespace Microsoft.SharedSource.SqlServer.MDDE.ManagementStudio
{
    public partial class UpdateTemplateInstance : Form
    {
        string repositoryConnectionString;
        string templateName;
        string templateInstanceName;
        XmlDocument configurableComponents;
        string operationLog = string.Empty;
        XmlDocument changesXml;
        XmlDocument templateTree;
        XmlElement instanceRoot;
        bool changesXmlLocked = false;

        public UpdateTemplateInstance(
            string repositoryConnectionString,
            string templateName, string templateInstanceName)
        {
            InitializeComponent();
            this.repositoryConnectionString = repositoryConnectionString;
            this.templateName = templateName;
            this.templateInstanceName = templateInstanceName;
        }

        private void UpdateTemplateInstance_Load(object sender, EventArgs e)
        {
            // Display the basic information.
            SqlConnectionStringBuilder cb =
                new SqlConnectionStringBuilder(
                this.repositoryConnectionString);

            lblRepository.Text =
                cb.DataSource + "." + cb.InitialCatalog;

            lblTemplate.Text = this.templateName;

            lblTemplateInstance.Text = this.templateInstanceName;

            // Display wait message in the tree view.
            trvConfigurableComponents.Nodes.Add("Retrieving data...");

            // Gray out all controls except 'Cancel' here.
            tabMain.Enabled = false;
            btnOK.Enabled = false;
            
            // Start off the background worker.
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(
            object sender, DoWorkEventArgs e)
        {
            // Fetch the configurable components list for the template.
            OperationResult result =
                MDDEManager.GetTemplateConfigurableComponents(
                this.repositoryConnectionString, this.templateName);

            OperationResult resultTemplateTree =
                MDDEManager.GetTemplateTree(this.repositoryConnectionString,
                this.templateName);

            if (result.SuccessStatus == false)
            {
                configurableComponents = null;
                operationLog = result.OperationLog[0];
                return;
            }
            else
            {
                configurableComponents =
                    (XmlDocument)result.OperationOutputParameters[0];
                templateTree =
                    (XmlDocument)resultTemplateTree.OperationOutputParameters[0];
            }

            // Get the instance configuration data.
            result =
                MDDEManager.GetInstanceConfigurationData(
                repositoryConnectionString, templateName,
                templateInstanceName);

            if (result.SuccessStatus == false)
            {
                changesXml = null;
                operationLog = result.OperationLog[0];
            }
            else
            {
                changesXml =
                    (XmlDocument)result.OperationOutputParameters[0];
                instanceRoot =
                    (XmlElement)changesXml.FirstChild;
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            tabMain.Enabled = true;
            btnOK.Enabled = true;            

            // Load the component tree nodes.
            trvConfigurableComponents.Nodes.Clear();

            if (configurableComponents == null)
            {
                MessageBox.Show(
                    "Error getting the configurable " +
                    "components information. \n" +
                    operationLog,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                this.Close();

                return;
            }

            if (changesXml == null)
            {
                MessageBox.Show(
                    "Error getting the template instance information. \n" +
                    operationLog,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                this.Close();

                return;
            }

            // Get the root template node and add it to treeview.
            XmlNode xmlRoot =
                configurableComponents.SelectSingleNode("//Template");
            TreeNode trvRoot =
                trvConfigurableComponents.Nodes.Add(
                xmlRoot.Attributes["ComponentName"].Value);
            trvRoot.Name = trvRoot.Text;
            trvRoot.Tag = xmlRoot;

            // Create treenodes for each configurable component.
            foreach (XmlNode xmlConfigComp in xmlRoot.SelectNodes(
                "./ConfigurableComponent"))
            {
                TreeNode trvConfigComp = new TreeNode(
                    xmlConfigComp.Attributes["ComponentName"].Value);
                trvConfigComp.Name = trvConfigComp.Text;
                trvConfigComp.Tag = xmlConfigComp;
                trvRoot.Nodes.Add(trvConfigComp);
            }

            trvConfigurableComponents.ExpandAll();

            // Force select the first tree node.
            if (trvConfigurableComponents.Nodes.Count > 0)
                trvConfigurableComponents.SelectedNode =
                    trvConfigurableComponents.Nodes[0];
        }

        private void trvConfigurableComponents_AfterSelect(
            object sender, TreeViewEventArgs e)
        {
            if (e.Node.Tag == null)
                return;

            XmlElement templateComp = (XmlElement)e.Node.Tag;
            string componentType =
                templateComp.Attributes["ComponentType"].Value;
            string componentName =
                templateComp.Attributes["ComponentName"].Value;
            string componentUri =
                templateComp.HasAttribute("ComponentUri") ?
                templateComp.Attributes["ComponentUri"].Value :
                string.Empty;

            // Lock the changes xml.
            changesXmlLocked = true;

            // Clear off controls.
            dgProperties.Rows.Clear();
            dgMappings.Rows.Clear();
            trvCollection.SelectedNode = trvCollection.Nodes[0];
            trvCollection.Nodes[0].Nodes["Input Collection"].Nodes.Clear();
            trvCollection.Nodes[0].Nodes["Output Collection"].Nodes.Clear();
            dgCollections.Rows.Clear();
            lblComponentName.Text = string.Empty;
            lblComponentType.Text = string.Empty;
            rtxComponentUri.Text = string.Empty;

            // Change the label texts.
            lblComponentName.Text = e.Node.Name;
            lblComponentType.Text =
                templateComp.Attributes["ComponentType"].Value;
            rtxComponentUri.Text = componentUri;

            // Proceed only if we have a configurable node selected.
            if (templateComp.Attributes["ComponentType"].Value == "Template")
            {
                dgMappings.Enabled = false;
                tpgMappings.Cursor = Cursors.No;

                trvCollection.SelectedNode = trvCollection.Nodes[0];
                trvCollection.Enabled = false;
                dgCollections.Enabled = false;
                tpgCollections.Cursor = Cursors.No;

                // Release changes xml.
                changesXmlLocked = false;
                return;
            }

            // If the template does not define any 
            // mappings, disable mappings grid.
            if (templateComp.Attributes["ContainsMappings"].Value != "True")
            {
                dgMappings.Enabled = false;
                tpgMappings.Cursor = Cursors.No;
            }
            else
            {
                dgMappings.Enabled = true;
                tpgMappings.Cursor = Cursors.Default;
            }

            // If the template does not define any 
            // custom collections, disable collections grid.
            if (templateComp.Attributes["ContainsCollections"].Value != "True")
            {
                trvCollection.Enabled = false;
                dgCollections.Enabled = false;
                tpgCollections.Cursor = Cursors.No;
            }
            else
            {
                trvCollection.Enabled = true;
                dgCollections.Enabled = true;
                tpgCollections.Cursor = Cursors.Default;
            }


            XmlNode treeComponent = templateTree.SelectSingleNode(
                    "//*[@Uri=\"" + componentUri + "\"]");
            // Populate properties grid.
            // Get all the node scalar properties.
            foreach (XmlNode xnodeProperty in
                templateComp.SelectNodes("./ConfigurableProperty"))
            {
                // Check if the changes xml has this property node.
                XmlNode changesXmlProperty =
                    changesXml.SelectSingleNode(
                    "//*[@URL=\"" + componentUri +
                    "\"]/Properties/Property[@Name=\"" +
                    xnodeProperty.Attributes["Name"].Value + "\"]");

                // If not, add the property with original value into the grid.
                if (changesXmlProperty == null)
                {
                    // Get the property from the template tree.
                    XmlNode templateTreeProperty = treeComponent.SelectSingleNode(
                        "./Property[@Name=\"" + xnodeProperty.Attributes["Name"].Value + "\"]");

                    dgProperties.Rows.Add(
                        xnodeProperty.Attributes["Name"].Value,
                        (templateTreeProperty == null) ? String.Empty :
                        templateTreeProperty.Attributes["Value"].Value);                                     
                }
                else
                    dgProperties.Rows.Add(
                        xnodeProperty.Attributes["Name"].Value,
                        changesXmlProperty.Attributes["Value"].Value);
            }

            // If there are associated mappings, populate the mappings grid.
            
            XmlNode changesXmlMappings =
                changesXml.SelectSingleNode("//*[@URL=\"" + componentUri + "\"]/Mappings");

            XmlNodeList templateTreeMappings =
                templateTree.SelectNodes("//*[@Uri=\"" + componentUri + "\"]/Mappings/Mapping");

            // TODO: Populate all the mappings from template and changeXml
            // Apply the changeXml mapping on template and show all the mappings.

            if (changesXmlMappings != null)
            {
                foreach (XmlNode changesXmlMapping in changesXmlMappings.SelectNodes("./Mapping"))
                    dgMappings.Rows.Add(
                        changesXmlMapping.Attributes["SourceColumn"].Value,
                        Convert.ToBoolean(changesXmlMapping.
                        Attributes["Usage"].Value) ? false : true,
                        changesXmlMapping.Attributes["DestinationColumn"].Value);
            }
            else
            {
                if (templateTreeMappings.Count > 0)
                    foreach (XmlNode templateTreeMapping in templateTreeMappings)
                        dgMappings.Rows.Add(
                            templateTreeMapping.Attributes["SourceColumn"].Value,
                            Convert.ToBoolean(templateTreeMapping.
                            Attributes["Usage"].Value) ? false : true,
                            templateTreeMapping.Attributes["DestinationColumn"].Value);
            }

            
            // Look for the collection information in changes xml.
            XmlNode changesXmlCollectionInfo =
                changesXml.SelectSingleNode("//*[@URL=\"" + componentUri + "\"]/CollectionInformation");
            

            // TODO: Populate all the collections from both template and changeXml
            // Apply the changeXml collections on template and show all the collections.

            // Create a collection information node if not found.
            if (changesXmlCollectionInfo == null)
            {
                XmlNode templateTreeComponent =
                    templateTree.SelectSingleNode("//*[@Uri=\"" + componentUri + "\"]");

                changesXmlCollectionInfo =
                    changesXml.CreateElement("CollectionInformation");

                #region Add in the collections.

                XmlNodeList templateTreeCollections = templateTreeComponent.SelectNodes(
                    "./Node[@Name=\"Inputs\" or @Name=\"Outputs\"]/Node[@Type=\"Input\" or @Type=\"Output\"]");

                if (templateTreeCollections.Count > 0)
                    foreach (XmlNode templateTreeCollection in templateTreeCollections)
                    {
                        // Add in the Collection node.
                        XmlElement changesXmlCollection =
                            UIHelper.AppendChildElement(
                            changesXmlCollectionInfo,
                            "Collection",
                            new string[]{
                        "Name",
                        templateTreeCollection.Attributes["Name"].Value,
                        "Type",
                        templateTreeCollection.Attributes["Type"].Value});

                        #region Add in collection properties.

                        XmlNodeList templateTreeCollectionProperties =
                            templateTreeCollection.SelectNodes("./Property");

                        if (templateTreeCollectionProperties.Count > 0)
                        {
                            XmlElement changesXmlCollectionProperties =
                                UIHelper.AppendChildElement(
                                changesXmlCollection,
                                "Properties", null);

                            foreach (XmlNode packageCollectionProperty in
                                templateTreeCollectionProperties)
                            {
                                XmlElement changesXmlCollectionProperty =
                                    UIHelper.AppendChildElement(
                                    changesXmlCollectionProperties,
                                    "Property",
                                    new string[]{
                                "Name",
                                packageCollectionProperty.Attributes["Name"].Value,
                                "Value",
                                packageCollectionProperty.Attributes["Value"].Value});
                            }
                        }

                        #endregion

                        #region Add in the columns.

                        XmlNodeList templateTreeColumns = templateTreeCollection.SelectNodes(
                            "./Node[@Name=\"Columns\"]/Node[@Type=\"Column\"]");

                        if (templateTreeColumns.Count > 0)
                        {
                            XmlElement changesXmlColumns =
                                UIHelper.AppendChildElement(
                                changesXmlCollection, "Columns", null);

                            foreach (XmlNode templateTreeColumn in templateTreeColumns)
                            {
                                XmlElement changesXmlColumn =
                                    UIHelper.AppendChildElement(
                                    changesXmlColumns, "Column",
                                    new string[] { 
                                        "Name", templateTreeColumn.Attributes["Name"].Value });

                                // Add in the column properties.
                                XmlNodeList templateTreeColumnProperties =
                                    templateTreeColumn.SelectNodes(
                                    "./Property");

                                if (templateTreeColumnProperties.Count > 0)
                                {
                                    XmlElement changesXmlColumnProperties =
                                        UIHelper.AppendChildElement(
                                        changesXmlColumn, "Properties", null);

                                    foreach (XmlNode packageColumnProperty
                                        in templateTreeColumnProperties)
                                        UIHelper.AppendChildElement(
                                            changesXmlColumnProperties,
                                            "Property",
                                            new string[]{
                                        "Name",
                                        packageColumnProperty.Attributes["Name"].Value,
                                        "Value",
                                        packageColumnProperty.Attributes["Value"].Value});
                                }
                            }
                        }

                        #endregion
                    }
                #endregion
            }

            // Populate the treeview inputs.
            foreach (XmlNode xnodeInput in
                changesXmlCollectionInfo.SelectNodes(
                "./Collection[@Type=\"Input\"]"))
            {
                TreeNode tnodeColumnCollection =
                    trvCollection.Nodes[0].
                    Nodes["Input Collection"].Nodes.Add(
                    xnodeInput.Attributes["Name"].Value);
                tnodeColumnCollection.Tag = xnodeInput;

                // Add in the columns.
                foreach (XmlNode xnodeColumn in
                    xnodeInput.SelectNodes(
                    "./Columns/Column"))
                {
                    TreeNode tnodeColumn =
                        tnodeColumnCollection.Nodes.Add(
                        xnodeColumn.Attributes["Name"].Value);
                    tnodeColumn.Tag = xnodeColumn;
                }
            }

            // Populate the treeview outputs.
            foreach (XmlNode xnodeOutput in
                changesXmlCollectionInfo.SelectNodes(
                "./Collection[@Type=\"Output\"]"))
            {
                TreeNode tnodeColumnCollection =
                    trvCollection.Nodes[0].
                    Nodes["Output Collection"].Nodes.Add(
                    xnodeOutput.Attributes["Name"].Value);
                tnodeColumnCollection.Tag = xnodeOutput;

                // Add in the columns.
                foreach (XmlNode xnodeColumn in
                    xnodeOutput.SelectNodes(
                    "./Columns/Column"))
                {
                    TreeNode tnodeColumn =
                        tnodeColumnCollection.Nodes.Add(
                        xnodeColumn.Attributes["Name"].Value);
                    tnodeColumn.Tag = xnodeColumn;
                }
            }

            // Finally, tag the treeview.
            trvCollection.Tag = changesXmlCollectionInfo;        

            // Release changes xml.
            changesXmlLocked = false;
        }

        private void trvCollection_AfterSelect(
            object sender, TreeViewEventArgs e)
        {
            dgCollections.Rows.Clear();

            #region Enabling/disabling of controls.
            btnAddInput.Enabled = false;
            btnRemoveInput.Enabled = false;
            btnAddOutput.Enabled = false;
            btnRemoveOutput.Enabled = false;
            btnAddColumn.Enabled = false;
            btnRemoveColumn.Enabled = false;
            dgCollections.Enabled = false;

            if (e.Node.Name == "Input Collection")
            {
                btnAddInput.Enabled = true;
            }
            else if (e.Node.Name == "Output Collection")
            {
                btnAddOutput.Enabled = true;
            }
            else if (e.Node.Tag != null)
            {
                XmlNode xnodePropertyHost = (XmlNode)e.Node.Tag;
                switch (xnodePropertyHost.Name)
                {
                    case "Collection":
                        btnAddColumn.Enabled = true;
                        dgCollections.Enabled = true;
                        if (xnodePropertyHost.Attributes["Type"].
                            Value == "Input")
                        {
                            btnRemoveInput.Enabled = true;
                        }
                        if (xnodePropertyHost.Attributes["Type"].
                            Value == "Output")
                        {
                            btnRemoveOutput.Enabled = true;
                        }
                        break;

                    case "Column":
                        btnRemoveColumn.Enabled = true;
                        dgCollections.Enabled = true;
                        break;
                }

                #region Grid population

                foreach (XmlNode xnodeProperty
                    in xnodePropertyHost.SelectNodes(
                    "./Properties/Property"))
                {
                    dgCollections.Rows.Add(
                        xnodeProperty.Attributes["Name"].Value,
                        xnodeProperty.Attributes["Value"].Value);
                }

                #endregion
            }
            #endregion
        }

        private void dgProperties_CellValueChanged(
            object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex < 0 || e.RowIndex > dgProperties.Rows.Count - 1)
                return;

            // Return if the changes xml is locked.
            if (changesXmlLocked)
                return;

            XmlElement templateComp =
                (XmlElement)trvConfigurableComponents.SelectedNode.Tag;
            string componentName =
                templateComp.Attributes["ComponentName"].Value;
            string componentType =
                templateComp.Attributes["ComponentType"].Value;
            string componentUri =
                templateComp.HasAttribute("ComponentUri") ?
                templateComp.Attributes["ComponentUri"].Value :
                string.Empty;

            // There is difference in the component types of package tree and changes xml. 
            // Since the template configurations are generated from package tree, they get 
            // component types from their package trees.
            // Here translate the component types for package tree and template configurable
            // components to changes xml types.
            switch (componentType)
            {
                case "ConnectionManager":
                    componentType = "ConnectionManager";
                    break;
                case "Executable":
                case "Component":
                    componentType = "Component";
                    break;
                case "Variable":
                    componentType = "Variable";
                    break;
            }

            UIHelper.AppendComponentProperty(
                instanceRoot, componentName,
                componentType, componentUri,
                dgProperties.Rows[e.RowIndex].
                Cells["propertyName"].Value.ToString(),
                (string)dgProperties.Rows[e.RowIndex].
                Cells["propertyValue"].Value);
        }

        private void dgMappings_RowValidating(
            object sender, DataGridViewCellCancelEventArgs e)
        {
            // Validate the row, unless its a new row.
            if (dgMappings.Rows[e.RowIndex].IsNewRow)
                return;

            // Do nothing if the changes xml is locked.
            if (changesXmlLocked)
                return;

            // Check that the source column value is specified.
            if (dgMappings.Rows[e.RowIndex].
                Cells["sourceColumn"].Value == null)
            {
                // Flag error message.
                MessageBox.Show("Source column name cannot be empty.",
                    "Validation Failure",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);

                // Stay in the same row.
                e.Cancel = true;

                // Return.
                return;
            }

            // Verify that same source/destination 
            // column is not entered twice.

            for (int i = 0; i < dgMappings.Rows.Count; i++)
            {
                // Continue if it is the same/new row.
                if (i == e.RowIndex || dgMappings.Rows[i].IsNewRow)
                    continue;

                // If there is already this source column.
                // NOTE: We are doing case-sensitive comparison here
                // so there might be chances of user entering two same
                // source column names.
                if (dgMappings.Rows[i].Cells["sourceColumn"].
                    Value.ToString().Equals(
                    dgMappings.Rows[e.RowIndex].
                    Cells["sourceColumn"].Value.ToString()))
                {
                    // Flag error message.
                    MessageBox.Show("The source column already exists.",
                        "Validation Failure",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);

                    // Cancel event.
                    e.Cancel = true;

                    // Return
                    return;
                }

                // If there is already this destination column
                if (dgMappings.Rows[i].Cells["destinationColumn"].
                    Value != null &&
                    dgMappings.Rows[e.RowIndex].Cells["destinationColumn"].
                    Value != null &&
                    dgMappings.Rows[i].Cells["destinationColumn"].
                    Value.ToString() ==
                    dgMappings.Rows[e.RowIndex].
                    Cells["destinationColumn"].Value.ToString())
                {
                    // Flag error message.
                    MessageBox.Show("The destination column already exists.",
                        "Validation Failure",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);

                    // Cancel event.
                    e.Cancel = true;

                    // Return
                    return;
                }
            }
        }

        private void btnAddInput_Click(object sender, EventArgs e)
        {
            AddCollection("Input");
        }

        private void btnAddOutput_Click(object sender, EventArgs e)
        {
            AddCollection("Output");
        }

        private void btnAddColumn_Click(object sender, EventArgs e)
        {
            // Locate the parent collection.
            XmlNode xnodeColumnCollection =
            (XmlNode)trvCollection.SelectedNode.Tag;

            // Locate the 'Columns' node.
            XmlNode xnodeColumns =
                xnodeColumnCollection.SelectSingleNode("./Columns");

            if (xnodeColumns == null)
                xnodeColumns =
                    UIHelper.AppendChildElement(
                    xnodeColumnCollection, "Columns", null);

            // Add the 'Column' element 
            // to 'Columns' element.
            XmlElement xnodeColumn =
                UIHelper.AppendChildElement(xnodeColumns, "Column",
                new string[]{
                    "Name",
                    Guid.NewGuid().ToString()});

            // Create a new treenode.
            TreeNode tnodeColumn =
                trvCollection.SelectedNode.Nodes.Add(
                xnodeColumn.Attributes["Name"].Value);

            // Add the xmlnode to treenode as a tag.
            tnodeColumn.Tag = xnodeColumn;

            // Select the new tree node.
            trvCollection.SelectedNode = tnodeColumn;

            // Bring the treenode in editable mode.
            tnodeColumn.BeginEdit();
        }

        private void dgCollections_Leave(object sender, EventArgs e)
        {
            // Return if the changes xml is locked.
            if (changesXmlLocked)
                return;

            // Recreate the 'Property' list 
            // in the associated property host.

            // Get the property host.
            XmlNode xnodePropertyHost =
                (XmlNode)trvCollection.SelectedNode.Tag;

            // Return if we do not have a property host.
            if (xnodePropertyHost == null)
                return;

            // Clear off all the properties.
            foreach (XmlNode xnodeProperty in
                xnodePropertyHost.SelectNodes(
                "./Properties/Property"))
                xnodeProperty.ParentNode.RemoveChild(xnodeProperty);

            // Recreate fresh properties.
            foreach (DataGridViewRow r in dgCollections.Rows)
            {
                if (r.Cells["dgCollectionsPropertyName"].
                    EditedFormattedValue.ToString() == "Name" ||
                    r.IsNewRow)
                    continue;

                // Locate element 'Properties'.
                XmlNode xnodeProperties =
                    xnodePropertyHost.SelectSingleNode("./Properties");
                if (xnodeProperties == null)
                    xnodeProperties =
                        UIHelper.AppendChildElement(
                        xnodePropertyHost, "Properties", null);

                // Create the 'Property' element.
                XmlElement xnodeProperty =
                    UIHelper.AppendChildElement(xnodeProperties, "Property",
                    new string[]{
                        "Name",
                        r.Cells["dgCollectionsPropertyName"].
                    EditedFormattedValue.ToString(),
                        "Value",
                        r.Cells["dgCollectionsPropertyValue"].
                    EditedFormattedValue.ToString()});
            }
        }

        private void dgCollections_RowValidating(
            object sender, DataGridViewCellCancelEventArgs e)
        {
            if (changesXmlLocked)
                return;

            // Validate the row, unless its a new row.
            if (e.RowIndex > dgCollections.Rows.Count)
                return;
            if (dgCollections.Rows[e.RowIndex].IsNewRow)
                return;

            // Check that the property name is specified.
            if (dgCollections.Rows[e.RowIndex].
                Cells["dgCollectionsPropertyName"].Value == null)
            {
                // Flag error message.
                MessageBox.Show("Property name cannot be empty.",
                    "Validation Failure",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);

                // Stay in the same row.
                e.Cancel = true;

                // Return.
                return;
            }

            // Verify that the same property name is not entered twice.
            for (int i = 0; i < dgCollections.Rows.Count; i++)
            {
                // Continue if it is the same/new row.
                if (i == e.RowIndex || dgCollections.Rows[i].IsNewRow)
                    continue;

                // If there is already this property name
                if (dgCollections.Rows[i].Cells["dgCollectionsPropertyName"].
                    Value.ToString().Equals(dgCollections.Rows[e.RowIndex].
                    Cells["dgCollectionsPropertyName"].Value.ToString(),
                    StringComparison.InvariantCultureIgnoreCase))
                {
                    // Flag error message.
                    MessageBox.Show("The property name already exists.",
                        "Validation Failure",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Exclamation);

                    // Cancel event.
                    e.Cancel = true;

                    // Return
                    return;
                }
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            // Pass on the template instance configurations to middleware
            // so that it can update the template instance in the database.
            OperationResult result =
                MDDEManager.UpdateTemplateInstance(
                repositoryConnectionString, templateName,
                templateInstanceName, changesXml);

            if (result.SuccessStatus == false)
            {
                StringBuilder errorLog = new StringBuilder();
                foreach (string logEntry in result.OperationLog)
                    errorLog.Append(logEntry);

                MessageBox.Show("Template instance could not be updated.\n" +
                    errorLog.ToString(), "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return;
            }
            else
            {
                MessageBox.Show("Template instance updated successfully.",
                    "Success", MessageBoxButtons.OK,
                    MessageBoxIcon.Information);

                this.Close();
            }
        }

        private void btnRemoveInput_Click(object sender, EventArgs e)
        {
            RemoveCollectionElement();
        }

        private void btnRemoveOutput_Click(object sender, EventArgs e)
        {
            RemoveCollectionElement();
        }

        private void btnRemoveColumn_Click(object sender, EventArgs e)
        {
            RemoveCollectionElement();
        }

        private void RemoveCollectionElement()
        {
            // Locate the element node in instance configurations.
            XmlNode xnodeColumn =
            (XmlNode)trvCollection.SelectedNode.Tag;

            // Remove this node from the document.
            xnodeColumn.ParentNode.RemoveChild(xnodeColumn);

            // Get the parent tree node.
            TreeNode parentNode = trvCollection.SelectedNode.Parent;

            // Remove the associated element node from treeview.
            parentNode.Nodes.Remove(trvCollection.SelectedNode);

            // Select the parent node.
            trvCollection.SelectedNode = parentNode;
        }

        private void dgMappings_Leave(object sender, EventArgs e)
        {
            XmlElement templateComp =
                (XmlElement)trvConfigurableComponents.SelectedNode.Tag;
            string componentType =
                templateComp.Attributes["ComponentType"].Value;
            string componentName =
                templateComp.Attributes["ComponentName"].Value;
            string componentUri =
                templateComp.HasAttribute("ComponentUri") ?
                templateComp.Attributes["ComponentUri"].Value :
                string.Empty;

            if (string.IsNullOrEmpty(componentUri))
                return;

            // There is difference in the component types of package tree and changes xml. 
            // Since the template configurations are generated from package tree, they get 
            // component types from their package trees.
            // Here translate the component types for package tree and template configurable
            // components to changes xml types.
            switch (componentType)
            {
                case "ConnectionManager":
                    componentType = "ConnectionManager";
                    break;
                case "Executable":
                case "Component":
                    componentType = "Component";
                    break;
                case "Variable":
                    componentType = "Variable";
                    break;
            }

            // Locate the component in changes xml.
            XmlElement changesXmlComponent =
                (XmlElement)UIHelper.AppendComponent(
                instanceRoot, componentType, componentName, componentUri);

            // Locate mappings in changes xml.
            XmlElement changesXmlMappings =
                (XmlElement)changesXmlComponent.SelectSingleNode(
                "./Mappings");

            if (changesXmlMappings == null)
                changesXmlMappings =
                    UIHelper.AppendChildElement(
                    changesXmlComponent, "Mappings", null);

            // Clear off the mappings.
            // NOTE: This statement assumes that 'Mappings' 
            // node does not have any default attributes.
            changesXmlMappings.RemoveAll();

            // Repopulate the mappings.
            foreach (DataGridViewRow r in dgMappings.Rows)
            {
                if (r.IsNewRow)
                    continue;

                string destinationColumn =
                    r.Cells["destinationColumn"].
                    EditedFormattedValue == null ?
                    string.Empty :
                    r.Cells["destinationColumn"].
                    EditedFormattedValue.ToString();

                bool blockPassThru =
                    ((r.Cells["blockPassThru"].EditedFormattedValue != null) &&
                    (Convert.ToBoolean(
                    r.Cells["blockPassThru"].EditedFormattedValue) == true)) ?
                    true : false;

                UIHelper.AppendChildElement(
                    changesXmlMappings, "Mapping",
                    new string[]{
                        "Usage",
                        blockPassThru?
                        "False":"True",
                        "SourceColumn",
                        r.Cells["sourceColumn"].
                        EditedFormattedValue.ToString(),
                        "DestinationColumn",
                        destinationColumn});
            }
        }

        private void trvCollection_Validating(object sender, CancelEventArgs e)
        {
            // TODO: Verify that a column with same name is not added twice.
        }

        private void trvCollection_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (e.Node.Tag == null ||
                ((XmlNode)e.Node.Tag).Name != "Collection" &&
                ((XmlNode)e.Node.Tag).Name != "Column")
                e.CancelEdit = true;
        }

        private void trvCollection_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            // Change the name of newly added node.
            if (!string.IsNullOrEmpty(e.Label))
                ((XmlNode)e.Node.Tag).Attributes["Name"].Value = e.Label;
        }

        private void AddCollection(string type)
        {
            XmlElement templateComp =
                (XmlElement)trvConfigurableComponents.SelectedNode.Tag;
            string componentType =
                templateComp.Attributes["ComponentType"].Value;
            string componentName =
                templateComp.Attributes["ComponentName"].Value;
            string componentUri =
                templateComp.HasAttribute("ComponentUri") ?
                templateComp.Attributes["ComponentUri"].Value :
                string.Empty;

            // Create a 'Collection' element.
            XmlElement xCollection =
                (XmlElement)UIHelper.AppendCollection(
                instanceRoot, componentName, componentType,
                componentUri, type, Guid.NewGuid().ToString());

            // Create a new treenode.
            TreeNode tnodeInput =
                trvCollection.SelectedNode.Nodes.Add(
                xCollection.Attributes["Name"].Value);

            // Add the xmlnode to treenode as a tag.
            tnodeInput.Tag = xCollection;

            // Select the new tree node.
            trvCollection.SelectedNode = tnodeInput;

            // Bring the treenode in editable mode.
            tnodeInput.BeginEdit();
        }

    }
}